/* 
 * File:   Compiler.cpp
 * Author: itu
 * 
 * Created on November 26, 2011, 9:49 PM
 */

#include "Compiler.h"
#include <algorithm>
#include <iterator>
#include <iomanip>
#include <sstream>

Compiler::Compiler() {
    //cout << "Compiler created." << endl;
    dolarSymbol = new Terminal("$");
    nullSymbol = new Terminal("€");
    stateNumber = 0;
    ruleNumber = 0;
}

void Compiler::printNonterminal() {
    for (int i = 0; i < nonTerminals.size(); i++)
        cout << nonTerminals[i]->getIdentifier() << endl;
}

void Compiler::printTerminal() {
    for (int i = 0; i < terminals.size(); i++)
        cout << terminals[i]->getIdentifier() << endl;
}

void Compiler::printNonterminalRules() {
    for (int i = 0; i < nonTerminals.size(); i++) {
        cout << nonTerminals[i]->getIdentifier() << " rules" << endl;
        nonTerminals[i]->printRules();
        cout << endl;
    }
}

void Compiler::printNonterminalFirst() {
    //cout<<"nonterminals size: "<<nonTerminals.size()<<endl;
    for (int i = 0; i < nonTerminals.size(); i++) {
        cout << nonTerminals[i]->getIdentifier() << " firsts" << endl;
        nonTerminals[i]->findFirst();
        nonTerminals[i]->printFirstSet();
        cout << endl;
    }
}

Nonterminal* Compiler::getNonterminalByIdentifier(string identifier) {
    Nonterminal *nonTerm;
    bool alreadyInList = false;
    for (int i = 0; i < nonTerminals.size(); i++) {
        if (identifier.compare(nonTerminals[i]->getIdentifier()) == 0) {
            //cout<<identifier<<" is in Nonterminal list"<<endl;
            alreadyInList = true;
            nonTerm = nonTerminals[i];
            //cout<<"adres: "<<nonTerm<<endl;
            break;
        }
    }
    if (!alreadyInList) { //yoksa burda yaratıp döndürücem
        //cout<<identifier<<" is not in Nonterminal list"<<endl;
        nonTerm = new Nonterminal(identifier); //burası çalışıack yeni nonterm yaratılıp adresi döndürülecek
        if (nonTerminals.size() == 0)
            nonTerm->setAsStart();
        nonTerminals.push_back(nonTerm);
    }
    return nonTerm;
}

Terminal* Compiler::getTerminalByIdentifier(string identifier) {
    Terminal *term;
    bool alreadyInList = false;
    for (int i = 0; i < terminals.size(); i++) {
        if (identifier.compare(terminals[i]->getIdentifier()) == 0) {
            //cout<<identifier<<" is in terminal list"<<endl;
            alreadyInList = true;
            term = terminals[i];
            //cout<<"adres: "<<term<<endl;
            break;
        }
    }
    if (!alreadyInList) { //yoksa burda yaratıp döndürücem
        //cout<<identifier<<" is not in terminal list"<<endl;
        term = new Terminal(identifier); //burası çalışıack yeni nonterm yaratılıp adresi döndürülecek
        terminals.push_back(term);
    }
    return term;
}

void Compiler::printNonterminalFollow() {
    findNonterminalsFollow();
    for (int i = 0; i < nonTerminals.size(); i++) {
        cout << nonTerminals[i]->getIdentifier() << " follows" << endl;
        nonTerminals[i]->printFollowSet();
        cout << endl;
    }
}

/* Bir nonterminalin izle kümesini hesaplamak için türetim kurallarının sağ tarafları araştırılır.
 * Nonterminali, türetim kuralının sağ tarafında içeren kurallar getirilir. Kuralın son elamanı çekilir.
 * Son eleman izle kümesini aradığımız nonterminal ise türetim kuralına sahip nonterminalin izle kümesi 
 * hesaplanarak elemanları izle kümesine eklenir.Son eleman başka birşey ise ilk kümesinin elemanları
 * izle kümesine eklenir. Son eleman nullable ise türetim kuralına sahip nonterminalin izle kümesi hesaplanır
 * elemanları izle kümesine eklenir.
 */

void Compiler::findNonterminalsFollow() {
    for (int i = 0; i < nonTerminals.size(); i++) {
        if (nonTerminals[i]->checkIsStart())
            nonTerminals[i]->addToFollow(dolarSymbol);
        for (int j = 0; j < nonTerminals.size(); j++) {
            vector<Rule> rules;
            rules = nonTerminals[j]->getRules();
            for (int k = 0; k < rules.size(); k++) {
                if (rules[k].hasSymbol(nonTerminals[i])) {
                    //cout<<"In "<<nonTerminals[j]->getIdentifier()<<" rule has symbol "<<nonTerminals[i]->getIdentifier()<<endl;
                    vector<Symbol*> symbols;
                    symbols = rules[k].getSymbols();
                    //cout<<"Symbol back is:"<<symbols.back()->getIdentifier()<<" benim aradigim:"<<nonTerminals[i]->getIdentifier()<<endl;
                    if (symbols.back()->getIdentifier().compare(nonTerminals[i]->getIdentifier()) == 0) {
                        vector<Symbol*> follow;
                        follow = nonTerminals[j]->getFollowSet();
                        for (int x = 0; x < follow.size(); x++) {
                            nonTerminals[i]->addToFollow(follow[x]);
                        }
                    } else {
                        bool finded = false;
                        for (int y = 0; y < symbols.size(); y++) {
                            if (symbols[y]->getIdentifier().compare(nonTerminals[i]->getIdentifier()) == 0) {
                                //cout<<symbols[y]<< " ile "<<nonTerminals[i]->getIdentifier()<<" karsilastiridi sonuc:true"<<endl;
                                finded = true;
                                continue;
                            }
                            if (finded) {
                                vector<Symbol*> first;
                                first = symbols[y]->getFirstSet();
                                for (int x = 0; x < first.size(); x++) {
                                    //cout<<symbols.back()->getIdentifier()<<" nin first setinden gelen: "<<first[x]->getIdentifier()<<endl;
                                    nonTerminals[i]->addToFollow(first[x]);
                                }
                                break;
                            }
                        }
                    }
                }
            }
        }
    }
}

Nonterminal* Compiler::getStartNonterminal(){
    Nonterminal* nonterm;
    for(int i=0;i<nonTerminals.size(); i++){
        if (nonTerminals[i]->checkIsStart()) {
            nonterm = nonTerminals[i];
            break;
        }
    }
    return nonterm;
}

void Compiler::addStateToStates(State *aState){
    states.push_back(aState);
    stateNumber++;
}

bool Compiler::findPossibleGoTosForGivenState(State *aState){
    vector<Item*> items;
    Item *newItem;
    Symbol *symbol;
    State *newState;
    
    bool newStateFounded = false;
    
    //cout<<"-------------"<<endl;
    //cout<<aState->getIdentifier()<<" icin tum gecisler hesaplanacak"<<endl;
    //cout<<aState->getIdentifier()<<" tum itemlerin durumu:"<<aState->isAllItemFounded()<<endl;
    if(!(aState->isAllItemFounded()))
        aState->findAllItems();
    
    /** Tüm parçalar oluşturulmuş ise **/
    if(aState->isAllItemFounded()){
        
        items = aState->getItems();
        
        //cout<<"Taranacak itemler"<<endl;
        //aState->printItems();
        
        
        /** tüm parçaları iterate et **/
        for(int i=0; i<items.size(); i++){
            
            //cout<<"Taranan item:";
            //items[i]->printSymbols();
            //cout<<endl;
            
            /** parçada noktadan sonra gelen sembolü tut geçiş sembolü bu olacak **/
            symbol = items[i]->getNextSymbolFromDot();
            
            //cout<<"noktadan sonraki sembol:"<<symbol->getIdentifier()<<endl;

            /**kuralın sonuna gelindi mi kontrolu yap gelidyse yaratma **/
            if(items[i]->getDotLocation()<items[i]->getSymbols().size()){
                
                /** noktanın yerini bir arttırarak parçayı yarat **/
                newItem = new Item(items[i],items[i]->getDotLocation()+1);
                for(int x=0; x<items[i]->getNextSymbols().size(); x++){
                    newItem->addSymbolToNextSymbols(items[i]->getNextSymbols()[x]);
                }
                
                //cout<<"Yaratılan yeni item:";
                //newItem->printSymbols();
                //cout<<endl;
                
                /** sembol ile daha önce geçiş yapılmışsa yaratılan parçayı geçiş yapılan state'e ekle **/
                if(!(aState->isSymbolOnTheMapThenAddItemToState(symbol,newItem))){
                    
                    
                    //cout<<"Sembol ile daha once gecis yapilmamis"<<endl;
                    
                    bool founded = false;
                    int foundedIndex = 0;
                    
                    //cout<<"Statelerin su an ki itemleri"<<endl;
                    //printStates();
                    
                    /** varolan tüm statelerde bu parçayı ara **/
                    for(int j=0; j<states.size(); j++){
                        //cout<<"State dizisinin boyutu:"<<states.size()<<endl;
                        //cout<<"Kontrol edilen state:"<<states[j]->getIdentifier()<<endl;
                        if((states[j]->isItemOnTheList(newItem))){
                            //cout<<"Yaratilan item daha once "<<states[j]->getIdentifier()<<" da bulundu"<<endl;
                            founded = true;
                            foundedIndex = j;
                        }
                    }
                    
                    /** statelerde varsa şu anki state'in gotomapine ekle sembolü ve state'i ekle  **/
                    if(founded){
                        
                        aState->addGoTo(symbol, states[foundedIndex]);
                        states[foundedIndex]->addItemToItems(newItem);
                        
                        //cout<<aState->getIdentifier()<<" durumuna "<<symbol->getIdentifier()<<" -> "<<states[foundedIndex]->getIdentifier()<<" gecisi eklendi"<<endl;
                        
                        /** statelerde yoksa yeni state yarat şu an ki state'in gotomapine sembolü ve state'i ekle **/    
                    }else{
                        newState = new State(stateNumber);
                        newState->addItemToItems(newItem);
                        addStateToStates(newState);
                        aState->addGoTo(symbol, newState);
                        newStateFounded = true;
                        
                        //cout<<"Yeni state yaratildi idsi:"<<newState->getIdentifier()<<endl;
                        //cout<<"State'e item eklendi:";
                        //newItem->printSymbols();
                        //cout<<aState->getIdentifier()<<" durumuna "<<symbol->getIdentifier()<<" -> "<<newState->getIdentifier()<<" gecisi eklendi"<<endl;
                        //delete newState;
                    }
                }else{
                    //cout<<"sembol ile daha once gecis yapilmis"<<endl;
                }
                //cout<<"State dizisinin boyutu:"<<states.size()<<endl;
                //delete newItem;
            }
        }
    }
    
    return newStateFounded;
}

void Compiler::findAllGoTos(){
    for(int i=0; i<states.size(); i++){
        findPossibleGoTosForGivenState(states[i]);
    }
}

void Compiler::createDFA(){
    /** Başlangıç nonterminali bulunur. **/
    Nonterminal *startNonterminal = getStartNonterminal();
    
    /** Başlangıç kuralı kullanılarak başlangıç parçası yaratılır. **/
    Item *startItem = new Item(&startNonterminal->getRules()[0],0);
    
    /** Başlangıç parçasının sonraki sözcüğüne $ sözcüğü eklenir. **/
    startItem->addSymbolToNextSymbols(dolarSymbol);
    
    /** İlk durum yaratılır, listeye eklenir, ilk duruma başlangıç parçası eklenir. **/
    State *firstState = new State(stateNumber);
    firstState->addItemToItems(startItem);
    addStateToStates(firstState);
    
    /** ilk durumun tüm parçaları oluşturulur **/
    firstState->findAllItems();
    
    //firstState->printItems();
    
    /** ilk durumun olası tüm geçişleri hesaplanır **/
    findAllGoTos();
    //findPossibleGoTosForGivenState(firstState);
    printStates();
}

void Compiler::printStates(){
    for(int i=0; i<states.size(); i++){
        //cout<<states[i]->getIdentifier()<<" all item found: "<< states[i]->isAllItemFounded() <<endl;
        states[i]->printItems();
        states[i]->printGoToMap();
    }
}


void Compiler::printParserTable(){
    TableNode *aNode;
    cout<<"durum";
    for(int i=0; i<terminals.size(); i++){
        cout<<"\t"<<terminals[i]->getIdentifier();
    }
    cout<<"\t "<<dolarSymbol->getIdentifier()<<"  ";
    for(int i=0; i<nonTerminals.size(); i++){
        if(!(nonTerminals[i]->checkIsStart()))
            cout<<"\t"<<nonTerminals[i]->getIdentifier();
    }
    cout<<endl;
    for(int i=0; i<states.size(); i++){
        cout<<"    "<<states[i]->getIdentifier();
        for(int j=0; j<terminals.size(); j++){
            if(states[i]->isSymbolOnTheMap(terminals[j])){
                cout<<"\t"<<"o"<<states[i]->getMapStateBySymbol(terminals[j])->getIdentifier();
                aNode = new TableNode(terminals[j], states[i], SHIFT, states[i]->getMapStateBySymbol(terminals[j]));
                parserTable.push_back(aNode);
            }else if(states[i]->isSymbolReducible(terminals[j])){
                cout<<"\t"<<"i"<<states[i]->getReductionBySymbol(terminals[j])->getNumber();
                aNode = new TableNode(terminals[j], states[i], REDUCE, states[i]->getReductionBySymbol(terminals[j]));
                parserTable.push_back(aNode);
            }
            else{
                 cout<<"\t"<<" ";
                aNode = new TableNode(terminals[j], states[i], EMPTY, (void *)"");
                parserTable.push_back(aNode);
            }
        }
        if(states[i]->getIdentifier()==1){
            cout<<"\t"<<"Kabul";
            aNode = new TableNode(dolarSymbol, states[i], FINAL, (void *)"Kabul");
            parserTable.push_back(aNode);
        }else if(states[i]->isSymbolReducible(dolarSymbol)){
            cout<<"\t"<<"i"<<states[i]->getReductionBySymbol(dolarSymbol)->getNumber();
            aNode = new TableNode(dolarSymbol, states[i], REDUCE, states[i]->getReductionBySymbol(dolarSymbol));
            parserTable.push_back(aNode);
        }
        else{
            cout<<"\t"<<"     ";
            aNode = new TableNode(dolarSymbol, states[i], EMPTY, (void*)"");
            parserTable.push_back(aNode);
        }
        for(int k=0; k<nonTerminals.size(); k++){
            if(!(nonTerminals[k]->checkIsStart())){
                if(states[i]->isSymbolOnTheMap(nonTerminals[k])){
                    cout<<"\t"<<"o"<<states[i]->getMapStateBySymbol(nonTerminals[k])->getIdentifier();
                    aNode = new TableNode(nonTerminals[k], states[i], SHIFT, states[i]->getMapStateBySymbol(nonTerminals[k]));
                    parserTable.push_back(aNode);
                }
                else{
                    cout<<"\t"<<" ";
                    aNode = new TableNode(nonTerminals[k], states[i], EMPTY, (void*)"");
                    parserTable.push_back(aNode);
                }
            }
        }
        cout<<endl;
    }
    /*
    cout<<"Unit test"<<endl;
    void* key;
    key=getKeyAt(terminals[0], states[0]);
    Operation op;
    op=getOperationAt(terminals[0], states[0]);
    if(op==EMPTY){
        key = (char*) " ";
        cout<<key<<endl;
    }else if(op==FINAL){
        key = (char*) "Kabul";
        cout<<key<<endl;
    }else if(op==REDUCE){
        Rule* arule;
        arule = (Rule*) key;
        cout<<"i"<<arule->getNumber()<<endl;
    }else if(op==SHIFT){
        State* astate;
        astate = (State*) key;
        cout<<"o"<<astate->getIdentifier()<<endl;
    }
    */
}

int Compiler::countRuleNumber(){
    ruleNumber++;
    return ruleNumber-1;
}


Operation Compiler::getOperationAt(Symbol *aSymbol, State *aState){
    Operation op;
    for (int i=0; i<parserTable.size(); i++) {
        if(parserTable[i]->column->getIdentifier().compare(aSymbol->getIdentifier())==0 && parserTable[i]->row->getIdentifier() == aState->getIdentifier()){
            op=parserTable[i]->op;
            break;
        }
    }
    return op;
}
void* Compiler::getKeyAt(Symbol *aSymbol, State *aState){
    void* key;
    for (int i=0; i<parserTable.size(); i++) {
        if(parserTable[i]->column->getIdentifier().compare(aSymbol->getIdentifier())==0 && parserTable[i]->row->getIdentifier() == aState->getIdentifier()){
            key=parserTable[i]->key;
            break;
        }
    }
    return key;
}

void Compiler::parseInput(string input){
    Symbol *symbol;
    State *state;
    Rule *rule;
    string nextSymbol = "";
    bool eof = false;
    string inputString;
    string productString;
    int count = 0;
    inputString = input;
    
    /** İlk durum yığına eklendi **/
    statesStack.push_back(states[0]);
    
    inputString = inputString.append("$");
    cout<<endl;
    cout<<"Giris katari:"<<input<<endl;
    cout<<endl;
    
    productString = input;
    productString = productString.append("<-");
    
    /** satır operatörü oluşturuldu **/
    
    
    cout<<setw(25)<<left<<"Turetim"<<setw(25)<<left<<"Yigin"<<setw(25)<<left<<"Giris"<<setw(25)<<left<<"Islem"<<endl;
    cout<<"-----------------------------------------------------------------------------------------------"<<endl;
    
    
    /** karakter karakter okuma gerçekleştirilecek **/
    while(inputString != "" && !eof){
        
        
        nextSymbol = inputString[0];
        
        /** karaktere ait sembol getiriliyor **/
        if(inputString.compare(0,1,"$")==0)
            symbol = dolarSymbol;
        else if(inputString.compare(0,1,"$") != 0)
            symbol = getTerminalByIdentifier(nextSymbol);
        
        nextSymbol = "";
        //cout<<statesStack.back()->getIdentifier()<<" da "<<symbol->getIdentifier()<<" aranacak"<<endl;
        cout<<setw(25)<<left<<productString;
        cout<<setw(25)<<left;
        string stackout;
            for(int i=0; i<statesStack.size(); i++){
                stringstream id;
                id<<statesStack[i]->getIdentifier();
                stackout.append(id.str());
                if(!symbolsStack.empty() && i<symbolsStack.size()){
                    stackout.append(symbolsStack[i]->getIdentifier());
                }
            }
        cout<<stackout;
        cout<<setw(25)<<left<<inputString;
        /** yığının sonundaki durumdan karakter ile geçiş var mı **/
        if(statesStack.back()->isSymbolOnTheMap(symbol)){
            
            /** varsa geçiş durumunu getir. **/ 
            state = statesStack.back()->getMapStateBySymbol(symbol);
            string out;
            stringstream integer;
            out = "Otele, ";
            integer<<state->getIdentifier();
            out.append(integer.str());
            out.append("'e gec");
            cout<<setw(25)<<left<<out<<endl;
            /** TO:DO Oteleme yapılacak **/
            symbolsStack.push_back(symbol);
            statesStack.push_back(state);
            
            inputString.erase(0,1);
            if(inputString=="")
                eof = true;
           /*
            if(!lo->hasNextString() && count < 2){
                eof = true;
                count++;
            }
            */
        /** Yığının sonundaki durumdan karakter ile geçiş yok **/    
        }else{
            
            /** Geçiş haritasında yok ama indirgeme olabilir **/
            if(getOperationAt(symbol, statesStack.back()) == REDUCE){
                
                /** TO:DO indirgeme yapılacak **/
                rule = (Rule*)getKeyAt(symbol, statesStack.back());
                string out;
                out = "Indirge ";
                out.append(rule->Rule::getRuleString());
                cout<<out;
                cout<<endl;
                for(int i=0; i<rule->getSymbols().size(); i++){
                    statesStack.pop_back();
                    symbolsStack.pop_back();
                }
                
                if (rule->getNonterminal()->getIdentifier().compare("S")==0) {
                    count = 0;
                }
                
                string astr;
                astr.append(rule->Rule::getRuleString());
                astr.erase(1);
                productString.replace(count,rule->getSymbols().size(),astr); 
                count++;
                if(count+2 == productString.size())
                    count=0;
                //nextSymbol = lo->getString();
                
                //cout<<setw(25)<<left<<productString;
                //cout<<setw(25)<<left;
                string stackout;
                for(int i=0; i<statesStack.size(); i++){
                    stringstream id;
                    id<<statesStack[i]->getIdentifier();
                    stackout.append(id.str());
                    stackout.append(" ");
                }
                //cout<<stackout;
                //cout<<setw(25)<<left<<inputString;
                if(statesStack.back()->isSymbolOnTheMap(rule->getNonterminal())){
                    
                    /** varsa geçiş durumunu getir. **/ 
                    state = statesStack.back()->getMapStateBySymbol(rule->getNonterminal());
                    string out;
                    stringstream integer;
                    out = "Otele, ";
                    integer<<state->getIdentifier();
                    out.append(integer.str());
                    out.append("'e gec");
                    //cout<<setw(25)<<left<<out<<endl;
                    /** TO:DO Oteleme yapılacak **/
                    symbolsStack.push_back(rule->getNonterminal());
                    statesStack.push_back(state);
                    
                }else{
                    cout<<"Indirgeme hatali"<<endl;
                    break;
                }
                
            }else if(getOperationAt(symbol, statesStack.back()) == FINAL){
                
                /** TO:DO Kabul durumu yapılacak **/
                cout<<"Kabul"<<endl;
                break;
                
            }else if(getOperationAt(symbol, statesStack.back()) == EMPTY){
                
                /** TO:DO Boş durumu yapılacak **/
                cout<<"Hata"<<endl;
                break;
            }
        }
    }
}
